package abel.wlmr.model.type;

import abel.wlmr.model.WlmrField;

/**
 * B.4.3.4.3.1 帧序列域SEQ定义
 * 帧序列域 SEQ 为 1 字节，用于᧿述帧之间的传输序列的变化规则，由于受报文长度限制，数据无法
 * 在一帧内传输，需要分成多帧传输（每帧都应有数据单元标识，都可以作为独立的报文处理）。 SEQ 定
 * 义如图 B.5。
 * D7 D6 D5 D4 D3−D0
 * TpV FIR FIN CON PSEQ⁄RSEQ
 * 
 * @author Abel
 *
 */
public class FrmSeqField extends WlmrField {

    private boolean tpv;
    private boolean fir = true;
    private boolean fin = true;
    private boolean con;
    private byte seq;

    /**
     * FIR: true; FIN: true; CON: false; TPV: false.
     */
    public FrmSeqField() {
        super(1);
    }

    @Override
    public int getValue() {
        if (seq < 0 || seq > 15) {
            throw new IllegalArgumentException("seq should be [0,15] but " + seq);
        }

        return (short) ((tpv ? 1 : 0) << 7
            | (fir ? 1 : 0) << 6
            | (fin ? 1 : 0) << 5
            | (con ? 1 : 0) << 4
            | seq);
    }

    @Override
    public void parseValue(int b) {
        seq = (byte) (0x0F & b);

        tpv = 1 == ((b >> 7) & 0x01);
        fir = 1 == (b >> 6 & 0x01);
        fin = 1 == (b >> 5 & 0x01);
        con = 1 == (b >> 4 & 0x01);
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format("{tpv:%s, fir:%s, fin:%s, con:%s, seq:%s}", tpv, fir, fin, con, seq);
    }

    /**
     * <pre>
     * B.4.3.4.3.2 帧时间标签有效位TpV
     * TpV=0：表示在附加信息域中无时间标签 Tp；
     * TpV=1：表示在附加信息域中带有时间标签 Tp（Tp 定义见本附录 B.4.3.4.6.4）。
     * Default to false.
     * </pre>
     * 
     * @return the tpv
     */
    public boolean isTpv() {
        return tpv;
    }

    /**
     * <pre>
     * B.4.3.4.3.2 帧时间标签有效位TpV
     * TpV=0：表示在附加信息域中无时间标签 Tp；
     * TpV=1：表示在附加信息域中带有时间标签 Tp（Tp 定义见本附录 B.4.3.4.6.4）。
     * Default to false.
     * </pre>
     * 
     * @param tpv
     *            the tpv to set
     */
    public void setTpv(boolean tpv) {
        this.tpv = tpv;
    }

    /**
     * <pre>
     * B.4.3.4.3.3 首帧标志FIR、末帧标志FIN
     * FIR：置“1”，报文的第一帧。
     * FIN：置“1”，报文的最后一帧。
     * FIR、 FIN 组合状态所表示的含义见表 B.6。
     * 表B.6 帧 标 志
     * FIR FIN 应用说明
     * 0 0 多帧：中间帧
     * 0 1 多帧：结束帧
     * 1 0 多帧：第 1 帧，有后续帧。
     * 1 1 单帧
     * Default to true.
     * </pre>
     * 
     * @return the fir
     */
    public boolean isFir() {
        return fir;
    }

    /**
     * <pre>
     * B.4.3.4.3.3 首帧标志FIR、末帧标志FIN
     * FIR：置“1”，报文的第一帧。
     * FIN：置“1”，报文的最后一帧。
     * FIR、 FIN 组合状态所表示的含义见表 B.6。
     * 表B.6 帧 标 志
     * FIR FIN 应用说明
     * 0 0 多帧：中间帧
     * 0 1 多帧：结束帧
     * 1 0 多帧：第 1 帧，有后续帧。
     * 1 1 单帧
     * Default to true.
     * </pre>
     * 
     * @param fir
     *            the fir to set
     */
    public void setFir(boolean fir) {
        this.fir = fir;
    }

    /**
     * <pre>
     * B.4.3.4.3.3 首帧标志FIR、末帧标志FIN
     * FIR：置“1”，报文的第一帧。
     * FIN：置“1”，报文的最后一帧。
     * FIR、 FIN 组合状态所表示的含义见表 B.6。
     * 表B.6 帧 标 志
     * FIR FIN 应用说明
     * 0 0 多帧：中间帧
     * 0 1 多帧：结束帧
     * 1 0 多帧：第 1 帧，有后续帧。
     * 1 1 单帧
     * Default to true.
     * </pre>
     * 
     * @return the fin
     */
    public boolean isFin() {
        return fin;
    }

    /**
     * <pre>
     * B.4.3.4.3.3 首帧标志FIR、末帧标志FIN
     * FIR：置“1”，报文的第一帧。
     * FIN：置“1”，报文的最后一帧。
     * FIR、 FIN 组合状态所表示的含义见表 B.6。
     * 表B.6 帧 标 志
     * FIR FIN 应用说明
     * 0 0 多帧：中间帧
     * 0 1 多帧：结束帧
     * 1 0 多帧：第 1 帧，有后续帧。
     * 1 1 单帧
     * Default to true.
     * </pre>
     * 
     * @param fin
     *            the fin to set
     */
    public void setFin(boolean fin) {
        this.fin = fin;
    }

    /**
     * <pre>
     * B.4.3.4.3.4 请求确认标志位CON
     * 在所收到的报文中， CON 位置“1”，表示需要对该帧报文进行确认；置“0”，表示不需要对该帧报
     * 文进行确认。
     * Default to false.
     * </pre>
     * 
     * @return the con
     */
    public boolean isCon() {
        return con;
    }

    /**
     * <pre>
     * B.4.3.4.3.4 请求确认标志位CON
     * 在所收到的报文中， CON 位置“1”，表示需要对该帧报文进行确认；置“0”，表示不需要对该帧报
     * 文进行确认。
     * Default to false.
     * </pre>
     * 
     * @param con
     *            the con to set
     */
    public void setCon(boolean con) {
        this.con = con;
    }

    /**
     * <pre>
      * B.4.3.4.3.5 启动帧序号PSEQ/响应帧序号RSEQ
      * a） 启动帧序号 PSEQ
      * PSEQ 取自 1 字节的启动帧计数器 PFC 的低 4 位计数值 0～15。
      * b） 启动帧帧序号计数器 PFC
      * 每一对启动站和从动站之间均有 1 个独立的、由 1 字节构成的启动帧帧序号计数器 PFC， 计数范围
      * 为 0～255，用于记录当前启动帧的序号。启动站每发送 1 帧报文，该计数器加 1，从 0～255 循环加 1
      * 递增；重发帧则不加 1。
      * c） 响应帧序号 RSEQ
      * 响应帧序号 RSEQ 以启动报文中的 PSEQ 作为第一个响应帧序号，后续响应帧序号在 RSEQ 的基础
      * 上循环加 1 递增，数值范围为 0～15。
      * d） 帧序号改变规则
      * 1） 启动站发送报文后，当一个期待的响应在规定的超时时间内没有被收到，如果允许启动站
      * 重发，则该重发的启动帧序号 PSEQ 不变。重发次数可设置，最多 3 次；重发次数为 0，
      * 则不允许重发。
      * 2） 当 TpV=0 时，如果从动站连续收到两个具有相同启动帧序号 PSEQ 的启动报文，通常意味
      * 着报文的响应未被对方站收到。在这种情况下，则重发响应（不必重新处理该报文）。
      * 3） 当 TpV=0 时，如果启动站连续收到两个具有相同响应帧序号 RSEQ 的响应帧，则不处理第
      * 二个响应。
     * </pre>
     * 
     * @return the pseq
     */
    public byte getSeq() {
        return seq;
    }

    /**
     * <pre>
      * B.4.3.4.3.5 启动帧序号PSEQ/响应帧序号RSEQ
      * a） 启动帧序号 PSEQ
      * PSEQ 取自 1 字节的启动帧计数器 PFC 的低 4 位计数值 0～15。
      * b） 启动帧帧序号计数器 PFC
      * 每一对启动站和从动站之间均有 1 个独立的、由 1 字节构成的启动帧帧序号计数器 PFC， 计数范围
      * 为 0～255，用于记录当前启动帧的序号。启动站每发送 1 帧报文，该计数器加 1，从 0～255 循环加 1
      * 递增；重发帧则不加 1。
      * c） 响应帧序号 RSEQ
      * 响应帧序号 RSEQ 以启动报文中的 PSEQ 作为第一个响应帧序号，后续响应帧序号在 RSEQ 的基础
      * 上循环加 1 递增，数值范围为 0～15。
      * d） 帧序号改变规则
      * 1） 启动站发送报文后，当一个期待的响应在规定的超时时间内没有被收到，如果允许启动站
      * 重发，则该重发的启动帧序号 PSEQ 不变。重发次数可设置，最多 3 次；重发次数为 0，
      * 则不允许重发。
      * 2） 当 TpV=0 时，如果从动站连续收到两个具有相同启动帧序号 PSEQ 的启动报文，通常意味
      * 着报文的响应未被对方站收到。在这种情况下，则重发响应（不必重新处理该报文）。
      * 3） 当 TpV=0 时，如果启动站连续收到两个具有相同响应帧序号 RSEQ 的响应帧，则不处理第
      * 二个响应。
     * </pre>
     * 
     * @param ps
     *            the pseq to set
     */
    public void setSeq(byte ps) {
        if (ps < 0 || ps > 15) {
            throw new IllegalArgumentException("seq should be [0, 15]");
        }

        this.seq = ps;
    }
}
